; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; Verify that strnlen calls with constant string arguments and offsets
; and constant bounds are folded correctly.
;
; RUN: opt < %s -passes=instcombine -S | FileCheck %s

declare i64 @strnlen(ptr, i64)

@ax = external global [0 x i8]
@s5 = constant [6 x i8] c"12345\00"
@s5_3 = constant [9 x i8] c"12345\00xyz"


; Verify that the strnlen pointer argument is not annotated nonnull when
; nothing is known about the bound.

define i64 @no_access_strnlen_p_n(ptr %ptr, i64 %n) {
; CHECK-LABEL: @no_access_strnlen_p_n(
; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(ptr [[PTR:%.*]], i64 [[N:%.*]])
; CHECK-NEXT:    ret i64 [[LEN]]
;
  %len = call i64 @strnlen(ptr %ptr, i64 %n)
  ret i64 %len
}


; Verify that the strnlen pointer argument is annotated dereferenceable(1)
; (and not more) when the constant bound is greater than 1.

define i64 @access_strnlen_p_2(ptr %ptr) {
; CHECK-LABEL: @access_strnlen_p_2(
; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(ptr noundef nonnull dereferenceable(1) [[PTR:%.*]], i64 2)
; CHECK-NEXT:    ret i64 [[LEN]]
;
  %len = call i64 @strnlen(ptr noundef nonnull dereferenceable(1) %ptr, i64 2)
  ret i64 %len
}


; Verify that the strnlen pointer argument is annotated nonnull etc.,
; when the bound is known to be nonzero.

define i64 @access_strnlen_p_nz(ptr %ptr, i64 %n) {
; CHECK-LABEL: @access_strnlen_p_nz(
; CHECK-NEXT:    [[NNZ:%.*]] = or i64 [[N:%.*]], 1
; CHECK-NEXT:    [[LEN:%.*]] = call i64 @strnlen(ptr noundef nonnull dereferenceable(1) [[PTR:%.*]], i64 [[NNZ]])
; CHECK-NEXT:    ret i64 [[LEN]]
;
  %nnz = or i64 %n, 1
  %len = call i64 @strnlen(ptr noundef nonnull dereferenceable(1) %ptr, i64 %nnz)
  ret i64 %len
}


; Fold strnlen(ax, 0) to 0.

define i64 @fold_strnlen_ax_0() {
; CHECK-LABEL: @fold_strnlen_ax_0(
; CHECK-NEXT:    ret i64 0
;
  %len = call i64 @strnlen(ptr @ax, i64 0)
  ret i64 %len
}


; Fold strnlen(ax, 1) to *ax ? 1 : 0.

define i64 @fold_strnlen_ax_1() {
; CHECK-LABEL: @fold_strnlen_ax_1(
; CHECK-NEXT:    [[STRNLEN_CHAR0:%.*]] = load i8, ptr @ax, align 1
; CHECK-NEXT:    [[STRNLEN_CHAR0CMP:%.*]] = icmp ne i8 [[STRNLEN_CHAR0]], 0
; CHECK-NEXT:    [[TMP1:%.*]] = zext i1 [[STRNLEN_CHAR0CMP]] to i64
; CHECK-NEXT:    ret i64 [[TMP1]]
;
  %len = call i64 @strnlen(ptr @ax, i64 1)
  ret i64 %len
}


; Fold strnlen(s5, 0) to 0.

define i64 @fold_strnlen_s5_0() {
; CHECK-LABEL: @fold_strnlen_s5_0(
; CHECK-NEXT:    ret i64 0
;
  %len = call i64 @strnlen(ptr @s5, i64 0)
  ret i64 %len
}


; Fold strnlen(s5, 4) to 4.

define i64 @fold_strnlen_s5_4() {
; CHECK-LABEL: @fold_strnlen_s5_4(
; CHECK-NEXT:    ret i64 4
;
  %len = call i64 @strnlen(ptr @s5, i64 4)
  ret i64 %len
}


; Fold strnlen(s5, 5) to 5.

define i64 @fold_strnlen_s5_5() {
; CHECK-LABEL: @fold_strnlen_s5_5(
; CHECK-NEXT:    ret i64 5
;
  %len = call i64 @strnlen(ptr @s5, i64 5)
  ret i64 %len
}


; Fold strnlen(s5, (size_t)-1) to 5.

define i64 @fold_strnlen_s5_m1() {
; CHECK-LABEL: @fold_strnlen_s5_m1(
; CHECK-NEXT:    ret i64 5
;
  %len = call i64 @strnlen(ptr @s5, i64 -1)
  ret i64 %len
}


; Fold strnlen(s5_3 + 4, 5) to 1.

define i64 @fold_strnlen_s5_3_p4_5() {
; CHECK-LABEL: @fold_strnlen_s5_3_p4_5(
; CHECK-NEXT:    ret i64 1
;
  %ptr = getelementptr [9 x i8], ptr @s5_3, i32 0, i32 4
  %len = call i64 @strnlen(ptr %ptr, i64 5)
  ret i64 %len
}


; Fold strnlen(s5_3 + 5, 5) to 0.

define i64 @fold_strnlen_s5_3_p5_5() {
; CHECK-LABEL: @fold_strnlen_s5_3_p5_5(
; CHECK-NEXT:    ret i64 0
;
  %ptr = getelementptr [9 x i8], ptr @s5_3, i32 0, i32 5
  %len = call i64 @strnlen(ptr %ptr, i64 5)
  ret i64 %len
}


; Fold strnlen(s5_3 + 6, 3) to 3.

define i64 @fold_strnlen_s5_3_p6_3() {
; CHECK-LABEL: @fold_strnlen_s5_3_p6_3(
; CHECK-NEXT:    ret i64 3
;
  %ptr = getelementptr [9 x i8], ptr @s5_3, i32 0, i32 6
  %len = call i64 @strnlen(ptr %ptr, i64 3)
  ret i64 %len
}


; Fold even the invalid strnlen(s5_3 + 6, 4) call where the bound exceeds
; the number of characters in the array.  This is arguably safer than
; making the library call (although the low bound makes it unlikely that
; the call would misbehave).

define i64 @call_strnlen_s5_3_p6_4() {
; CHECK-LABEL: @call_strnlen_s5_3_p6_4(
; CHECK-NEXT:    ret i64 3
;
  %ptr = getelementptr [9 x i8], ptr @s5_3, i32 0, i32 6
  %len = call i64 @strnlen(ptr %ptr, i64 4)
  ret i64 %len
}
